package com.ehotting.mqbd.core.monitor;

import com.alibaba.fastjson.JSON;
import com.ehotting.eaf.core.DateHelper;
import com.ehotting.eaf.core.snow.SnowflakeUtil;
import com.ehotting.mqbd.api.bean.solver.SolverStateBean;
import com.ehotting.mqbd.api.bean.solver.ModuleInfoBean;
import com.ehotting.mqbd.api.enums.ServerWorkStateEnum;
import com.ehotting.mqbd.core.logger.LOGGER_KEY;
import com.ehotting.mqbd.core.manager.ModuleManager;
import com.ehotting.mqbd.core.manager.SolverInfoManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;

import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ActiveManager {

    private Logger logger = LoggerFactory.getLogger(ActiveManager.class);

    private List<ModuleActiveAble> moduleActiveList = new ArrayList<>();

    private SolverInfoManager solverInfoManager;
    private ModuleManager moduleManager;

    private ExecutorService executorService = Executors.newFixedThreadPool(500);

    public void invoke(){
        logger.info("Start ServerActive");
        long startTime = System.currentTimeMillis();
        Map<String, List<SolverStateBean>> solverStateMap = solverInfoManager.getMapServerInfoList();
        Set<Map.Entry<String, List<SolverStateBean>>> serverSet =  solverStateMap.entrySet();
        for(Map.Entry<String, List<SolverStateBean>> solverStateEntry : serverSet){
            String module = solverStateEntry.getKey();
            executorService.execute(createModuleActiveThread( module,solverStateEntry));
        }

        logger.info("Finish ServerActive,cost time:{}",(System.currentTimeMillis()-startTime));
    }

    private Runnable createModuleActiveThread(String module, Map.Entry<String, List<SolverStateBean>> solverStateEntry){
        Runnable run = new Runnable() {
            @Override
            public void run() {
                try{
                    doModuleActive(module, solverStateEntry);
                }catch (Exception e){
                    logger.error("Run module["+module+"]",e);
                }
            }
        };
        return run;
    }

    public ModuleActiveAble get(String module){
        for(ModuleActiveAble moduleActive : moduleActiveList){
            if(moduleActive.getModule().equals(module)){
                return moduleActive;
            }
        }
        return null;
    }

    private void doModuleActive(String module,Map.Entry<String, List<SolverStateBean>> solverStateEntry){
        ModuleActiveAble moduleActive = this.get(module);
        if(moduleActive==null){
            logger.error("module[{}] active is null.",module);
            return;
        }

        ModuleInfoBean moduleInfo = moduleManager.getByModule(module);
        if(moduleInfo==null){
            logger.error("moduleInfo[{}] is null,return.",module);
            return;
        }

        List<SolverStateBean> solverStateList = solverStateEntry.getValue();
        for(SolverStateBean solverState : solverStateList){
            MDC.put(LOGGER_KEY.MODULE, module);
            MDC.put(LOGGER_KEY.TRACE_ID, SnowflakeUtil.nextId64());
            MDC.put(LOGGER_KEY.START_TIME, String.valueOf(System.currentTimeMillis()));
            logger.info("module[{}] state:{}",module, JSON.toJSONString(solverState));
            if(!isOutInterval(solverState,moduleInfo)){
                logger.error("Solver[{},ip:{},port:{}] is out of Interval[{}],continue.",module,solverState.getSolverInfo().getIp(),solverState.getSolverInfo().getPort(),moduleInfo.getIntervalTime());
                continue;
            }
            MDC.put(ModuleActiveAble.LOGGER_IP_KEY,solverState.getSolverInfo().getIp());
            MDC.put(ModuleActiveAble.LOGGER_PORT_KEY,solverState.getSolverInfo().getPort());
            Integer taskCount = -1;
            try {
                taskCount = moduleActive.doAction(solverState);
            }catch (Exception e){
                logger.error("doModuleActive[module:"+module+",ip:"+solverState.getSolverInfo().getIp()+",port:"+solverState.getSolverInfo().getPort()+"]",e);
            }finally {
                try{
                    save(taskCount,solverState,moduleInfo);
                }catch (Exception e){
                    logger.error("save[module:"+module+",ip:"+solverState.getSolverInfo().getIp()+",port:"+solverState.getSolverInfo().getPort()+"]",e);
                }
                moduleActive.getModuleActiveLogger().info();
            }
            logger.info("Module[{}],{} task getAllQueueSize:{}",module,solverState.getSolverInfo(),taskCount);
        }
    }

    private void save(Integer taskCount, SolverStateBean solverState, ModuleInfoBean moduleInfo){
        logger.info("Start save");
        solverState = solverInfoManager.get(solverState.getModule(),solverState.getSolverInfo().getIp());
        setSolvingCount(solverState,taskCount);
        handleState(taskCount,solverState,moduleInfo);
        logger.info("solverState:{}", JSON.toJSONString(solverState));
        solverInfoManager.save(solverState);
        logger.info("Finish save");
    }

    private void handleState(Integer taskCount, SolverStateBean solverState, ModuleInfoBean moduleInfo){
        logger.info("Start handleState");

        if(taskCount == ModuleActiveAble.TELNET_FAIL_MASK || taskCount == ModuleActiveAble.TELNET_SUCCESS_MASK){
            logger.info("Telnet[{}] active.",taskCount);
            if(taskCount == ModuleActiveAble.TELNET_FAIL_MASK ){
                solverState.addErrorTimes(moduleInfo);
            }
            if(taskCount == ModuleActiveAble.TELNET_SUCCESS_MASK ){
                solverState.setAvailable().setLastActiveTime(new Date());
                solverState.setSolvingCount(0);
            }
            return;
        }

        if(taskCount<0){
            logger.info("solverState.getErrorTimes():{}",solverState.getErrorTimes());
            solverState.addErrorTimes(moduleInfo);
        }else{
            solverState.setAvailable().setLastActiveTime(new Date());
        }
        logger.info("Finish handleState");
    }


    private void setSolvingCount(SolverStateBean solverState, int taskCount){
        logger.info("Start setSolvingCount.");

        if(taskCount == ModuleActiveAble.TELNET_FAIL_MASK || taskCount == ModuleActiveAble.TELNET_SUCCESS_MASK){
            logger.info("Telnet[{}] active,return.",taskCount);
            return;
        }

        if(taskCount==0){
            Date date = new Date();
            if(solverState.getWorkTime()==null){
                solverState.setSolvingCount(taskCount);
            }else{
                /**避免时间太短导致求解器状态还没更新，在这里设置为10秒间隔，繁忙状态切换为空闲状态**/
                if(solverState.getWorkingState()== ServerWorkStateEnum.BUSY
                        && (date.getTime()/1000-solverState.getWorkTime().getTime()/1000)>=10){
                    logger.info("state is busy,work time off 10 second.");
                    solverState.setSolvingCount(taskCount);
                }
            }
        }else{
            solverState.setSolvingCount(taskCount);
        }
        logger.info("Finish setSolvingCount.");
    }


    private boolean isOutInterval(SolverStateBean solverState, ModuleInfoBean sysModuleInfo){
        logger.info("Start isOutInterval.");
        if(sysModuleInfo.getIntervalTime()==null){
            /**默认300秒**/
            sysModuleInfo.setIntervalTime(300L);
        }

        if(solverState.getLastActiveTime()==null){
            logger.info("solverState[{}].getLastActiveTime() is null,return true.",solverState.getModule());
            return true;
        }

        logger.info("module[{}]-intervalTime:{}",sysModuleInfo.getModule(),sysModuleInfo.getIntervalTime());

        boolean isOutInterval = (((new Date()).getTime()/1000-solverState.getLastActiveTime().getTime()/1000)>=sysModuleInfo.getIntervalTime());

        logger.info("Module[{},{}] isOutInterval is {},lastActiveTime:{},intervalTime:{}",
                solverState.getModule(),
                solverState.getSolverInfo().getIp(),
                isOutInterval,
                DateHelper.toSeparatorStringyyyyMMddHHmmss(solverState.getLastActiveTime()),
                sysModuleInfo.getIntervalTime()
        );
        logger.info("isOutInterval:{}",isOutInterval);
        logger.info("Finish isOutInterval.");
        return isOutInterval;
    }

    public List<ModuleActiveAble> getModuleActiveList() {
        return moduleActiveList;
    }

    public void setSolverInfoManager(SolverInfoManager solverInfoManager) {
        this.solverInfoManager = solverInfoManager;
    }

    public void setModuleManager(ModuleManager moduleManager) {
        this.moduleManager = moduleManager;
    }
}
